#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/bitrev.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/blkdev.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/uio.h>
#include <linux/idr.h>
#include <linux/bsg.h>
#include <linux/slab.h>

#include<linux/of.h>

#define LED_NUM 1
#define LED_NAME "dts_led"

static void __iomem * CCM_CCGR1;
static void __iomem * IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04;
static void __iomem * IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04;
static void __iomem * GPIO1_DR;
static void __iomem * GPIO1_GDIR;



struct ledDev
{
    dev_t devid;
    int major;
    int minor;
    struct cdev led_cdev;
    struct class * class;
    struct device_node *node;
};
static struct ledDev led;

static char flag;
static ssize_t led_write(struct file *filp, const char __user *buf,
              size_t count, loff_t *ppos)
{
   u32 reg =0;
   copy_from_user(&flag,buf,1);
   if(flag==0)
   {
       reg = readl(GPIO1_DR);
       reg &= ~(1<<4);
       writel(reg,GPIO1_DR);
   }
   else
   {
       reg = readl(GPIO1_DR);
       reg |= (1<<4);
       writel(reg,GPIO1_DR);
   }
   return 0;
}
static int led_open(struct inode *inode, struct file *filp)
{
  return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
  return 0;
}

static const struct file_operations fop={
   .write = led_write,
   .open = led_open,
   .owner = THIS_MODULE,
   .release = led_release,
};
static void LED_GPIO_Init(void)
{
   u32 reg = 0,cnt=0,i=0;
   u32 *reg_buff = NULL;

   led.node = of_find_node_by_path("/led");
   cnt = of_property_count_elems_of_size(led.node,"reg",sizeof(u32));

   reg_buff = (u32 *)kmalloc(sizeof(u32)*cnt,GFP_KERNEL);
   
   of_property_read_u32_array(led.node,"reg",reg_buff,cnt);
   for(i=0;i<cnt;i++)
        printk("reg_buff(%d)=0x%x\r\n",i,reg_buff[i]);
    
   CCM_CCGR1 =ioremap(reg_buff[0],reg_buff[1]);
   IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04= ioremap(reg_buff[2],reg_buff[3]);
   IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04= ioremap(reg_buff[4],reg_buff[5]);
   GPIO1_DR= ioremap(reg_buff[6],reg_buff[7]);
   GPIO1_GDIR= ioremap(reg_buff[8],reg_buff[9]);

    kfree(reg_buff);

   //开led时钟
   reg  = readl(CCM_CCGR1);
   reg &=~(3<<26);
   reg |= 3<<26;
   writel(reg,CCM_CCGR1);
   //设置引脚属性
   writel(0x5,IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04);
   writel(0x10b0,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04);
   //设置GPIO方向
   reg  = readl(GPIO1_GDIR);
   reg |= 1<<4;
   writel(reg,GPIO1_GDIR);
   writel(0xFFFFFFFF,GPIO1_DR);
}
static void LED_GPIO_DeInit(void)
{
   u32 reg=0;

   //恢复引脚属性
   writel(0,IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04);
   writel(0,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04);
   //恢复GPIO方向
   reg  = readl(GPIO1_GDIR);
   reg &= ~(1<<4);
   writel(reg,GPIO1_GDIR);
   //清空数据寄存器
   reg  = readl(GPIO1_DR);
   reg &= ~(1<<4);
   writel(reg,GPIO1_DR);
   //关led时钟
   reg  = readl(CCM_CCGR1);
   reg &=~(3<<26);
   writel(reg,CCM_CCGR1);

   iounmap(CCM_CCGR1);
   iounmap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04);
   iounmap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04);
   iounmap(GPIO1_DR);
   iounmap(GPIO1_GDIR);
}
static int __init led_init(void)
{
    int ret = 0;
    LED_GPIO_Init();
    {
        if(led.major)
        {
            led.devid = MKDEV(led.major,led.minor);
            ret = register_chrdev_region(led.devid,LED_NUM,LED_NAME);
        }
        else
        {
            ret = alloc_chrdev_region(&led.devid,0,LED_NUM,LED_NAME);
        }
        if(ret< 0 )
        {
            printk("register ERROR!\r\n");
            goto distory_register;
        }
        led.major = MAJOR(led.devid);
        led.minor = MINOR(led.devid);
        printk("major:%d\tminor:%d\r\n",led.major,led.minor);
    }

    {
        cdev_init(&led.led_cdev,&fop);
        ret = cdev_add(&led.led_cdev,led.devid,LED_NUM);
        if(ret<0)
        {
            printk("dev add ERROR!\r\n");
            goto destroy_cdev;
        }
    }

    {
        led.class=class_create(THIS_MODULE,LED_NAME);
        device_create(led.class,NULL,led.devid,NULL,LED_NAME);
    }

    return 0;
distory_register:
    unregister_chrdev_region(led.devid,LED_NUM);
destroy_cdev:
    cdev_del(&led.led_cdev);
    return -1;
}
static void __exit led_exit(void)
{
    LED_GPIO_DeInit();
    unregister_chrdev_region(led.devid,LED_NUM);
    cdev_del(&led.led_cdev);
    device_destroy(led.class,led.devid);
    class_destroy(led.class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_ALIAS("xiao");
MODULE_LICENSE("GPL");
